package com.lonewalker.snail.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import com.lonewalker.snail.common.base.ApiResult;
import com.lonewalker.snail.constant.flow.ProcessConstant;
import com.lonewalker.snail.domain.entity.SysUser;
import com.lonewalker.snail.domain.request.*;
import com.lonewalker.snail.domain.vo.TaskVo;
import com.lonewalker.snail.service.ProcessTaskService;
import com.lonewalker.snail.util.AssertUtil;
import lombok.RequiredArgsConstructor;
import org.camunda.bpm.engine.HistoryService;
import org.camunda.bpm.engine.RuntimeService;
import org.camunda.bpm.engine.TaskService;
import org.camunda.bpm.engine.history.HistoricTaskInstance;
import org.camunda.bpm.engine.task.Task;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author lonewalker
 */
@RequiredArgsConstructor
@Service
public class ProcessTaskServiceImpl implements ProcessTaskService {

    private final TaskService taskService;
    private final HistoryService historyService;
    private final RuntimeService runtimeService;


    @Override
    public ApiResult<List<TaskVo>> listToDoTaskBySingle(SysUser userInfo, String businessKey) {
        String tenantId = userInfo.getTenantId().toString();
        String userId = userInfo.getId().toString();
        List<TaskVo> taskList = new ArrayList<>();
        List<Task> tasks = taskService.createTaskQuery()
                //没有租户概念就把这行去掉
                .tenantIdIn(tenantId)
                .processInstanceBusinessKey(businessKey)
                .taskAssignee(userId)
                .list();

        //Task只是个接口,直接返回会有序列化问题，但是可能和camunda版本也有关系
        if (CollUtil.isNotEmpty(tasks)) {
            TaskVo taskVo;
            for (Task task : tasks) {
                taskVo = new TaskVo();
                taskVo.setProcessInstanceId(task.getProcessInstanceId());
                taskVo.setTaskId(task.getId());
                taskList.add(taskVo);
            }
        }
        return ApiResult.success(taskList);
    }

    @Override
    public ApiResult<List<TaskVo>> listToDoTaskByMulti(SysUser userInfo, String[] businessKeys) {
        String tenantId = userInfo.getTenantId().toString();
        String userId = userInfo.getId().toString();
        List<TaskVo> taskList = new ArrayList<>();
        List<Task> tasks = taskService.createTaskQuery()
                //没有租户概念就把这行去掉
                .tenantIdIn(tenantId)
                .processInstanceBusinessKeyIn(businessKeys)
                .taskAssignee(userId)
                .list();

        //Task只是个接口,直接返回会有序列化问题，但是可能和camunda版本也有关系
        if (CollUtil.isNotEmpty(tasks)) {
            TaskVo taskVo;
            for (Task task : tasks) {
                taskVo = new TaskVo();
                taskVo.setProcessInstanceId(task.getProcessInstanceId());
                taskVo.setTaskId(task.getId());
                taskList.add(taskVo);
            }
        }
        return ApiResult.success(taskList);
    }

    @Override
    public ApiResult<List<TaskVo>> pageToDoTask(SysUser userInfo, String businessKey, Integer pageNum, Integer pageSize) {
        String tenantId = userInfo.getTenantId().toString();
        String userId = userInfo.getId().toString();
        pageNum = (pageNum - 1) * pageSize;
        List<TaskVo> taskList = new ArrayList<>();
        List<Task> tasks = taskService.createTaskQuery()
                //没有租户概念就把这行去掉
                .tenantIdIn(tenantId)
                .processInstanceBusinessKey(businessKey)
                .taskAssignee(userId)
                //这里两个参数意思是从第几行开始，一共需要几行
                .listPage(pageNum, pageSize);

        //Task只是个接口,直接返回会有序列化问题，但是可能和camunda版本也有关系
        if (CollUtil.isNotEmpty(tasks)) {
            TaskVo taskVo;
            for (Task task : tasks) {
                taskVo = new TaskVo();
                taskVo.setProcessInstanceId(task.getProcessInstanceId());
                taskVo.setTaskId(task.getId());
                taskList.add(taskVo);
            }
        }
        return ApiResult.success(taskList);
    }

    @Override
    public ApiResult<Map<String, String>> completeSingleTask(CompleteTaskRequest requestParam) {
        Task task = checkTask(requestParam.getProcessInstanceId(), requestParam.getTaskId());

        Map<String, String> map = MapUtil.newHashMap(4);
        map.put(ProcessConstant.NODE_ID, task.getTaskDefinitionKey());
        map.put(ProcessConstant.NODE_NAME, task.getName());

        taskService.complete(requestParam.getTaskId());
        return ApiResult.success(map);
    }

    @Override
    public ApiResult<Void> transferTask(TransferTaskRequest requestParam) {
        checkTask(requestParam.getProcessInstanceId(), requestParam.getTaskId());
        taskService.setAssignee(requestParam.getTaskId(), requestParam.getUserId());
        return ApiResult.successMessage("转办成功");
    }

    @Override
    public ApiResult<Void> delegateTask(DelegateTaskRequest requestParam) {
        checkTask(requestParam.getProcessInstanceId(), requestParam.getTaskId());

        taskService.delegateTask(requestParam.getTaskId(), requestParam.getUserId());
        return ApiResult.success();
    }

    @Override
    public ApiResult<Void> resolveTask(ResolveTaskRequest requestParam) {
        checkTask(requestParam.getProcessInstanceId(), requestParam.getTaskId());

        taskService.resolveTask(requestParam.getTaskId());
        return ApiResult.success();
    }

    @Override
    public ApiResult<List<TaskVo>> listDoneTask(SysUser userInfo, String businessKey) {
        List<TaskVo> taskList = new ArrayList<>();
        List<HistoricTaskInstance> historicTasks = historyService.createHistoricTaskInstanceQuery()
                .tenantIdIn(userInfo.getTenantId().toString())
                .processInstanceBusinessKey(businessKey)
                .taskAssignee(userInfo.getId().toString())
                .finished()
                .taskDeleteReason(ProcessConstant.COMPLETED)
                .list();

        if (CollUtil.isNotEmpty(historicTasks)) {
            TaskVo taskVo;
            for (HistoricTaskInstance historicTask : historicTasks) {
                taskVo = new TaskVo();
                taskVo.setProcessInstanceId(historicTask.getProcessInstanceId());
                taskVo.setTaskId(historicTask.getId());
                taskList.add(taskVo);
            }
        }

        return ApiResult.success(taskList);
    }

    @Override
    public ApiResult<String> addAssignee(AddAssigneeRequest requestParam) {
        //以4.bpmn的会签节点示例
        runtimeService.createProcessInstanceModification(requestParam.getProcessInstanceId())
                .startBeforeActivity(requestParam.getNodeId())
                .setVariable("manager", requestParam.getUserId())
                .execute();
        return ApiResult.successMessage("加签成功");
    }

    @Override
    public ApiResult<List<TaskVo>> listCandidateTask(SysUser userInfo) {
        List<TaskVo> taskVos = new ArrayList<>();
        List<Task> taskList = taskService.createTaskQuery().taskCandidateUser(userInfo.getId().toString()).list();
        if (CollUtil.isNotEmpty(taskList)){
            TaskVo taskVo;
            for (Task task : taskList) {
                taskVo = new TaskVo();
                taskVo.setProcessInstanceId(task.getProcessInstanceId());
                taskVo.setTaskId(task.getId());
                taskVos.add(taskVo);
            }
        }
        return ApiResult.success(taskVos);
    }

    @Override
    public ApiResult<String> claimTask(SysUser userInfo, String taskId) {
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        if (ObjectUtil.isNotNull(task)){
            taskService.claim(taskId,userInfo.getId().toString());
        }
        return ApiResult.successMessage("任务领取成功");
    }

    @Override
    public ApiResult<String> giveBackTask(SysUser userInfo, String taskId) {
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        if (ObjectUtil.isNotNull(task)){
            taskService.setAssignee(taskId,null);
        }
        return ApiResult.successMessage("任务归还成功");
    }

    /**
     * 检查任务是否存在
     *
     * @param processInstanceId 流程实例id
     * @param taskId            任务id
     * @return task
     */
    private Task checkTask(String processInstanceId, String taskId) {
        Task task = taskService.createTaskQuery().processInstanceId(processInstanceId).taskId(taskId).singleResult();
        AssertUtil.checkService(ObjectUtil.isNull(task), "任务不存在");
        return task;
    }
}
